package pers.cfeng.server.dataManage.page;


import pers.cfeng.server.dataManage.pageCache.PageCacheConstant;
import pers.cfeng.server.utils.ByteBufferParser;

import java.util.Arrays;

/**
 * 普通的page页面， 和首页不同，只是前2字节记录空闲地址偏移
 * 【tid中是前8字节当作长度】
 * [FreeSpaceOffset][Data]
 *
 * Page直接操作的时byte[], 真正写入文件是在PageCache中
 */
public class PageCommon {

    private static final short OFFSET_FREE = 0;
    private static final short OFFSET_DATA = 2;
    //总长度应该是PageSize - 首部偏移长度
    public static int MAX_FREE_SPACE = PageCacheConstant.PAGE_SIZE - OFFSET_DATA;

    //初始化页面
    public static byte[] initRaw() {
        byte[] raw = new byte[PageCacheConstant.PAGE_SIZE];
        setFSO(raw, OFFSET_DATA);
        return raw;
    }
    //偏移的地址移动是8KB, 2位即可表示，提取的数成为short类型
    private static void setFSO(byte[] raw, short ofData) {
        System.arraycopy(ByteBufferParser.short2Byte(ofData),0,raw,OFFSET_FREE, OFFSET_DATA);
    }

    //获取当前页面的FSO, 读取前两个字节转为short类型
    public static short getFSO(Page page) {
        return getFSO(page.getData());
    }

    private static short getFSO(byte[] raw) {
        return ByteBufferParser.parseShort(Arrays.copyOfRange(raw, 0 , 2));
    }

    //将raw插入page中，返回插入位置
    public static short insert(Page page, byte[] raw) {
        //内存中修改该页面内容，那么需要再移除时需要写回文件
        page.setModified(true);
        short offset = getFSO(page.getData());
        //先在内存中写到byte[]后面
        System.arraycopy(raw, 0, page.getData(), offset, raw.length);
        setFSO(page.getData(), (short)(offset + raw.length));
        return offset;
    }

    //获取页面空闲空间大小
    public static int getFreeSpace(Page page) {
        return PageCacheConstant.PAGE_SIZE - (int)getFSO(page.getData());
    }

    //数据库崩溃后重新打开时，恢复插入数据
    public static void recoverInsert(Page page, byte[] raw, short offset) {
        //写操作都代表当前的page被更改，需要回写保持一致
        page.setModified(true);
        System.arraycopy(raw, 0, page.getData(), offset, raw.length);
        short rawFso = getFSO(page.getData());
        if(rawFso < offset + raw.length) {
            setFSO(page.getData(), (short)(offset + raw.length));
        }
    }

    //恢复时更新数据
    public static void recoverUpdate(Page page, byte[] raw, short offset) {
        page.setModified(true);
        System.arraycopy(raw, 0, page.getData(), offset, raw.length);
    }

}
